package MiniREParser;

import java.util.ArrayList;
import java.util.List;

import table_walker.Token;

public class MINI_RE_Parser {
ArrayList<Token> tokenList;
ArrayList<List<String>> instructions;
ArrayList<String> line= new ArrayList<String>();
public MINI_RE_Parser(ArrayList<Token> t)
{
	tokenList=t;
	instructions= new ArrayList<List<String>>();
}

public ArrayList<List<String>> run()
{
	if(tokenList.get(0).getName().equals("begin"))
	{
		tokenList.remove(0);
		statementList();
	}
	else
	{
		//send error
		throw new Error("No begin statement");
	}
	if(!tokenList.get(0).getName().equals("end"))
	{
		//send error
		throw new Error("No end statement");

	}
	return instructions;
}

private void statementList() {
	statement();
	statementListTail();
}

private void statementListTail() {
	if(!tokenList.get(0).getName().equals("end"))
	{
	statement();
	statementListTail();
	}
	//else return do nothing (equivalent to returning epsilon)
}

private void statement() {
	 line= new ArrayList<String>();
	if(tokenList.get(0).getType().equals("$ID"))
	{
		consume();
		if(tokenList.get(0).getName().equals("="))
		{
			//consume();
			tokenList.remove(0);
			if(tokenList.get(0).getName().equals("find")||tokenList.get(0).getType().equals("$ID"))//talk with tri about this
			{
				line.add("=");
				exp();
				semicolonCheck();
				
			}
			else if(tokenList.get(0).getName().equals("#"))
			{
				consume();
				exp();
				semicolonCheck();
			}
			else if(tokenList.get(0).getName().equals("maxfreqstring"))
			{
				consume();
				if(tokenList.get(0).getName().equals("("))
				{
					tokenList.remove(0);
				}
				else
				{
					throw new Error("ERROR: doesn't fit format (ID)");

				}
				if(tokenList.get(0).getType().equals("$ID"))
				{
					consume();
				}
				if(tokenList.get(0).getName().equals(")"))
				{
					tokenList.remove(0);
				}
				else
				{
					throw new Error("ERROR: doesn't fit format (ID)");

				}
				semicolonCheck();
			}
			else
			{
				//send error
				throw new Error("Not a valid statement: Expect =,=#,maxfreqstring");

			}
		}
	}
	else if(tokenList.get(0).getName().equals("replace"))
	{
		consume();
		statement4();
		semicolonCheck();
	}
	else if(tokenList.get(0).getName().equals("recursivereplace"))
	{
		consume();
		statement5();
		semicolonCheck();
	}
	else if(tokenList.get(0).getName().equals("print"))//makesure parenthesis handeling is good
	{
		consume();
		if(tokenList.get(0).getName().equals("("))
		{
			tokenList.remove(0);
		}
		else
		{
			throw new Error("ERROR: doesn't fit format (<explist>)");

		}
		explist();
		if(tokenList.get(0).getName().equals(")"))
		{
			tokenList.remove(0);
		}
		else
		{
			throw new Error("ERROR: doesn't fit format (<explist>)");

		}
		semicolonCheck();
	}
	else
	{
		//send error
		throw new Error("Invalid statement: Expect ID,replace,recursivereplace, or print");

	}
	instructions.add(line);
}

private void semicolonCheck()
{
	if(tokenList.get(0).getName().equals(";"))
	{
		tokenList.remove(0);
	}
	else
	{
		throw new Error("Not a valid statement: Expect semicolon");

	}
}

private void explist() {
	exp();
	explisttail();
}

private void exp() {
	if(tokenList.get(0).getType().equals("$ID"))//should I worry about paranthesis?
	{
		consume();
		return;
	}
	else if(tokenList.get(0).getName().equals("("))
	{
		tokenList.remove(0);
		exp();
		if(tokenList.get(0).getName().equals(")"))
		{
			tokenList.remove(0);
		}
		else
		{
			throw new Error("ERROR: doesn't fit format (<exp>)");

		}
	}
	else
	{
		term();
		exptail();
		
	}
	
}


private void exptail() {
	 if(tokenList.get(0).getName().equals("diff")||tokenList.get(0).getName().equals("union")||tokenList.get(0).getName().equals("inters"))
	{
	binop();
	term();
	exptail();
	}
	//else do nothing. Equivilant to epsilon.
}

private void binop() {
	 if(tokenList.get(0).getName().equals("diff")||tokenList.get(0).getName().equals("union")||tokenList.get(0).getName().equals("inters"))
	 {
		 consume();
	 }
	 else
	 {
		 //send error
		throw new Error("ERROR:Expected binary operator");

	 }
}

private void term() {
	 if(tokenList.get(0).getName().equals("find"))
	{
		consume();
		if(tokenList.get(0).getType().equals("$REGEX"))
		{
			tokenList.get(0).setName(tokenList.get(0).getName().substring(1,tokenList.get(0).getName().length()-1));

			consume();
			if(tokenList.get(0).getName().equals("in"))
			{
				tokenList.remove(0);
				filename();
				return;
				
			}
			else
			{
				throw new Error("ERROR:expect keyword \"in\" after REGEX in find statement");

			}
			
		}
	}
	return;
}

private void explisttail()
{
	 if(tokenList.get(0).getName().equals(","))//if there is a comma then consume
	{
		tokenList.remove(0);
		exp();
		explisttail();
	}
	 //else do nothing (return epsilon
}

private void statement4() {

	if(tokenList.get(0).getType().equals("$REGEX"))
	{
		tokenList.get(0).setName(tokenList.get(0).getName().substring(1,tokenList.get(0).getName().length()-1));

		consume();
		if(tokenList.get(0).getName().equals("with"))
		{
			tokenList.remove(0);
			if(tokenList.get(0).getType().equals("$ASCII-STR"))
			{
				tokenList.get(0).setName(tokenList.get(0).getName().substring(1,tokenList.get(0).getName().length()-1));

				consume();
				if(tokenList.get(0).getName().equals("in"))
				{
					tokenList.remove(0);
					
						filenames();
						return;
					
				}
			}
		}
	}

//send error
	throw new Error("ERROR: replace format invalid");

}

private void statement5() {

	if(tokenList.get(0).getType().equals("$REGEX"))
	{
		tokenList.get(0).setName(tokenList.get(0).getName().substring(1,tokenList.get(0).getName().length()-1));

		consume();
		if(tokenList.get(0).getName().equals("with"))
		{
			tokenList.remove(0);
			if(tokenList.get(0).getType().equals("$ASCII-STR"))
			{
				tokenList.get(0).setName(tokenList.get(0).getName().substring(1,tokenList.get(0).getName().length()-1));

				consume();
				if(tokenList.get(0).getName().equals("in"))
				{
					tokenList.remove(0);
						filenames();
						return;
					
				}
			}
		}
	}

// send error
	throw new Error("ERROR: recursivereplace format invalid");

}


private void filenames() {
	sourcefile();
	if(tokenList.get(0).getName().equals(">!"))
	{
		tokenList.remove(0);
	}
	else
	{
		//send error
		throw new Error("ERROR:Expected >! seperating source and destination");

	}
	destinationfile();
}

private void filename()
{
	if(tokenList.get(0).getType().equals("$ASCII-STR"))
	{
		tokenList.get(0).setName(tokenList.get(0).getName().substring(1,tokenList.get(0).getName().length()-1));
		consume();
	}
	else
	{
		//send error
		throw new Error("Expected String type for  filename");
	}
}
private void destinationfile() {
	if(tokenList.get(0).getType().equals("$ASCII-STR"))
	{
		tokenList.get(0).setName(tokenList.get(0).getName().substring(1,tokenList.get(0).getName().length()-1));

		consume();
	}
	else
	{
		//send error
		throw new Error("Expected String type for destination file");
	}
}

private void sourcefile() {
	if(tokenList.get(0).getType().equals("$ASCII-STR"))
	{
		tokenList.get(0).setName(tokenList.get(0).getName().substring(1,tokenList.get(0).getName().length()-1));

		consume();
	}
	else
	{
		//send error
		throw new Error("Expected String type for source file");

	}
}

private void consume()
{
	if(tokenList.get(0).getName()!=null)//try to add name if it isn't null
	{
	line.add(tokenList.get(0).getName());
	}
	else
	{
		line.add(tokenList.get(0).getType());//else add the type(ID)
	}
	tokenList.remove(0);
}
}
